local dataJson = cjson.decode(KEYS[1]);
local data = dataJson["data"];
local commandId = dataJson["commandId"];
local tableNameKey = dataJson["tableNameKey"];
local primaryKeyValue = dataJson["primaryKeyValue"];
local bitMapKey = dataJson["mateDataBitMapKey"];
local indexTable = dataJson["indexMap"];
local mateDataLastUpdateTimeKey = dataJson["mateDataLastUpdateTimeKey"];
local mateDataIndexMap = dataJson["mateDataIndexMap"];
local mateDataMaxIdKey = dataJson["mateDataMaxIdKey"];

local isExecReplaceMaxId = false;
local resultV = false;

local typeConvert = function(value)
    if(type(value) == "nil")
    then
        return "null";
    end;

    if(type(value) == "boolean")
    then
        return tostring(value);
    end;

    if(type(value) == "table")
    then
        return cjson.encode(value);
    end;

    return value;
end;

local exectuRedis = function ()
    -- 基本数据
    redis.call("hset", tableNameKey, primaryKeyValue, cjson.encode(data));
    redis.call("setbit", bitMapKey, primaryKeyValue, 1);

    -- 记录最大的那个id
    local serverMaxId = redis.call("get", mateDataMaxIdKey);
    if(serverMaxId == false)
    then
        serverMaxId = 0;
    end;

    if(serverMaxId or serverMaxId < primaryKeyValue)
    then
        redis.call("set", mateDataMaxIdKey, primaryKeyValue);
        isExecReplaceMaxId = true;
    end;

    --[[
        元数据中修改时间使用 lua 取 redis 的本地时间，避免各个 tomcat 时间不同导致的bug。
        但是 lua 只能获取秒数 精度不够，而 redis 的 time 命令为随机类型命令，redis 禁止在 lua 中写入随机写。
        因为会导致主从不一致。所以这里就用 commandId 了
    --]]
    local currVersion = redis.call("set", mateDataLastUpdateTimeKey, commandId);

    -- 索引数据
    for key, value in pairs(indexTable) do
        redis.call("sadd", key, typeConvert(value));
    end;

    -- 元数据索引
    for key, value in pairs(mateDataIndexMap) do
        redis.call("sadd", key, typeConvert(value));
    end;

    resultV = true;
end;


--[[
    回滚.
    这里只要执行就说明并没有宕机，只是一些问题导致此次失败。
    如果回滚再失败应该交由客户端去做持久化，等到服务端健康，再去尝试。
    如果回滚失败，此条数据为脏数据。

    此处回滚可能在集群环境下，如果出现极端情况可能会出现不一致情况，这是 redis lua执行机制可能造成原因。
    如果A机器执行时候没有发送异常，在数据复制时候，B机器发生了异常，此时不一致就造成了。
    权衡利弊，在出现错误时候还是选择回滚来保证数据安全性，牺牲一致性。
--]]
local rollback = function ()
    -- 清理此次相关所有信息
    redis.call("hdel", tableNameKey, primaryKeyValue);
    redis.call("del", mateDataLastUpdateTimeKey);

    for key, value in pairs(indexTable) do
        redis.call("srem", key, typeConvert(value));
    end;

    for key, value in pairs(mateDataIndexMap) do
        redis.call("srem", key, typeConvert(value));
    end;
end;

local rollbackHandler = function ( err )
    xpcall(
        function()
           redis.log(redis.LOG_NOTICE, commandId, "***** script error info: ", err)
           redis.log(redis.LOG_NOTICE, commandId, "***** try rollback, table:", tableNameKey, ", primaryKey", primaryKeyValue)
           rollback();
           redis.log(redis.LOG_NOTICE, commandId, "***** success rollback")
        end,
        function(rollErr)
            redis.log(redis.LOG_NOTICE, commandId, "***** error rollback, data invalid. table:", tableNameKey, ", primaryKey", primaryKeyValue)
            redis.log(redis.LOG_NOTICE,rollErr);

            -- 回滚失败写入 redis 无效数据队列中，通过手动补偿的解决无效数据
            xpcall(
                function()
                   redis.log(redis.LOG_NOTICE, commandId, "***** try recordInvalidData")
                   recordInvalidData();
                   redis.log(redis.LOG_NOTICE, commandId, "***** success recordInvalidData")
                end,
                function(rollErr)
                    redis.log(redis.LOG_NOTICE, commandId, "***** error recordInvalidData It's over!!!!!", rollErr)
                end
            );
        end
    );
end;

--[[
    记录无效数据队列.
    如果记录出错那就无力回天了。
--]]
local recordInvalidData = function()
    -- TODO 待实现
    redis.call("hmset", "cache_sys_death", tableNameKey, primaryKeyValue);
end;

xpcall(exectuRedis, rollbackHandler);
return resultV;

